#!/usr/bin/python
# -*- coding: utf-8 -*-
from google.appengine.ext import db
from google.appengine.ext import webapp
from google.appengine.api import users
from google.appengine.api import urlfetch

import utils
import logging
import urllib
import yaml
import settings
import cgi
import urllib
import json
import random
import string

import time, os, os.path, datetime

import settings
import json
import cgi
import string

import ytalk.models as models


class ListRoomsChannelUri(webapp.RequestHandler):
    def get(self):
        s = models.ListRoomsSingleton.get_singleton()
        
        # test if channel exists
        try:
            resp = urlfetch.fetch('%s/%s/?rand=%f' % (settings.COMETURL, s.channel_id, random.random() ), method=urlfetch.GET, headers={}, allow_truncated=True)
        except urlfetch.DownloadError:
            resp = None
            
        # channel doesn't exist - create new one
        if resp and resp.status_code != 200:
            q = urllib.urlencode({
                'public':      'False',
                'disconnect_time': 8,
            })
            
            # this must go good - no try/except
            resp = urlfetch.fetch('http://comet.%s/comet/create_channel' % (settings.SERVICESURL), payload=q, method=urlfetch.POST, headers={}, allow_truncated=False)
            if resp.status_code != 200:
                logging.error("Failed to create channel. query=%r status=%i content=%.250r!" % (q, resp.status_code, resp.content) )
                self.error(500)
                return
        
            dd = yaml.load(resp.content)
            
            def mod(key):
                obj = db.get(key)
                # noone modified the record?
                if s.channel_id == obj.channel_id:
                    obj.channel_id      = dd['channel_id']
                    obj.event_key       = dd['event_key']
                    obj.destroy_key     = dd['destroy_key']
                    obj.events          = []
                    obj.last_event_id   = 0
                    obj.added_time      = datetime.datetime.utcnow()
                else:
                    raise db.Rollback()
                obj.put()
            db.run_in_transaction(mod, s.key())
            
            s = db.get(s.key())
        
        rooms = models.Room.all().order('-added').fetch(20)
        o = {
            'channel_id':       s.channel_id,
            'events':           map(lambda a:a.encode('utf-8'), s.events),
            'last_event_id':    s.last_event_id,
            'bigbox':           utils.render_to_mako('ytalk/ytalk_list_part.html', {'rooms':rooms}),
        }
        self.response.out.write(json.write(o))




class ListRoomsPartial(webapp.RequestHandler):
    def get(self):
        rooms = models.Room.all().order('-added').fetch(20)
        self.response.out.write(utils.render_to_mako('ytalk/ytalk_list_part.html', {
                'rooms': rooms, }))
        return


class ListRooms(webapp.RequestHandler):
    def get(self):
        s = models.ListRoomsSingleton.get_singleton()
        rooms = models.Room.all().order('-added').fetch(20)
        # garbage collection
        if len(rooms) > 10:
            todel = rooms[10:]
            for room in todel:
                if room.logged_users:
                    continue
                rooms.remove(room)
                room.delete()
        
        username = self.request.GET.get('username', '')
        if not username:
            username = self.request.cookies.get('username', '')
        username = filter(lambda c: c in (string.ascii_letters + string.digits + '-_'), username)
        if not username:
            username = 'user_' + utils.random_string(length=4)
        
        self.response.headers.add_header( 
                'Set-Cookie', 'username=%s; expires=Fri, 31-Dec-2020 23:59:59 GMT' % username.encode())
        
        roomname = 'room_' + utils.random_string(length=4)
        self.response.out.write(utils.render_to_mako('ytalk/ytalk_list.html', {
                'TOPDOMAIN':settings.TOPDOMAIN,
                'DOMAIN':settings.DOMAIN,
                'username': username,
                'new_room_name':roomname,
                'rooms': rooms,
                'max_id':s.last_event_id, }))
        return
    
    def post(self):
        s = models.ListRoomsSingleton.get_singleton()
        room_name   = self.request.POST.get('room_name', '')
        room_name   = filter(lambda c: c in (string.ascii_letters + string.digits + '-_'), room_name)
        if not room_name:
            room_name = 'room_' + utils.random_string(length=4)
        
        # unique room name
        nr_rooms = models.Room().all().filter('name =', room_name.encode()).count()
        if nr_rooms > 0:
            self.redirect('.')
            return
        
        room = models.Room()
        room.name           = room_name
        room.channel_id     = 'notset'
        room.secret         = utils.random_string()
        room.save()
        self.redirect('./%s/' % room.key() )
        last_event_id = send_event(s.channel_id,'refresh', s.event_key)
        def mod(key):
            obj = db.get(key)
            if obj.last_event_id == s.last_event_id:
                obj.last_event_id = last_event_id
            else:
                raise db.Rollback()
            obj.put()
        try:
            db.run_in_transaction(mod, s.key())
        except db.TransactionFailedError:
            pass
        return



def send_event(channel_id, msg, event_key):
    q = urllib.urlencode({
        'data':      msg,
        'event_key': event_key,
    })
    try:
        resp = urlfetch.fetch('http://comet.%s/comet/%s/event' % (settings.SERVICESURL, channel_id), payload=q, method=urlfetch.POST, headers={}, allow_truncated=False)
    except urlfetch.DownloadError:
        resp = None
        
    if not resp or resp.status_code != 200:
        logging.error("Sending event failed to channel %s event_key=%r message=%r" % (channel_id, event_key, msg) )
    
    dd = yaml.load(resp.content)
    return dd['event_id']













class RoomDetails(webapp.RequestHandler):
    def get(self, room_key):
        room = db.get(room_key)
        if not room:
            self.error(404)
            return
        
        # don't allow the channel to be overloaded
        if len(room.logged_users) > 4:
            self.redirect('../')
            return
        
        username = self.request.GET.get('username', '')
        if not username:
            username = self.request.cookies.get('username', '')
        username = filter(lambda c: c in (string.ascii_letters + string.digits + '-_'), username)
        if not username:
            username = 'user_' + utils.random_string(length=4)
        
        self.response.out.write(utils.render_to_mako('ytalk/ytalk_screen.html', {
                'TOPDOMAIN':settings.TOPDOMAIN,
                'username': username,
                'room': room }) )
        return



class RoomChannelUri(webapp.RequestHandler):
    def get(self, room_key):
        room = db.get(room_key)
        if not room:
            self.error(404)
            return
        
        # test if channel exists
        try:
            resp = urlfetch.fetch('%s/%s/?rand=%f' % (settings.COMETURL, room.channel_id, random.random() ), method=urlfetch.GET, headers={}, allow_truncated=True)
        except urlfetch.DownloadError:
            resp = None
            
        # channel doesn't exist - create new one
        if resp and resp.status_code != 200:
            uri = 'http://%s/ytalk/%s/user_action?secret=%s' % (settings.DOMAIN, room.key(), room.secret)
            q = urllib.urlencode({
                'public':      'True',
                #'event_callback_uri':uri,
                'login_callback_uri':uri,
                'logout_callback_uri':uri,
                'disconnect_time': 8,
            })
            
            # this must go good - no try/except
            resp = urlfetch.fetch('http://comet.%s/comet/create_channel' % (settings.SERVICESURL), payload=q, method=urlfetch.POST, headers={}, allow_truncated=False)
            if resp.status_code != 200:
                logging.error("Failed to create channel. query=%r status=%i content=%.250r!" % (q, resp.status_code, resp.content) )
                self.error(500)
                return
        
            dd = yaml.load(resp.content)
            
            def mod(key):
                obj = db.get(key)
                # noone modified the record?
                if room.channel_id == obj.channel_id:
                    obj.channel_id      = dd['channel_id']
                    obj.event_key       = dd['event_key']
                    obj.destroy_key     = dd['destroy_key']
                    obj.logged_users    = []
                    obj.events          = []
                    obj.last_event_id   = 0
                    obj.added_time      = datetime.datetime.utcnow()
                else:
                    raise db.Rollback()
                obj.put()
            db.run_in_transaction(mod, room.key())
            
            room = db.get(room.key())
        
        o = {
            'channel_id':       room.channel_id,
            #'events':           map(lambda a:a.encode('utf-8'), room.events),
            'logged_users':     room.logged_users,
            'last_event_id':    room.last_event_id,
        }
        self.response.out.write(json.write(o))


class RoomUserAction(webapp.RequestHandler):
    def get(self, room_key):
        room = db.get(room_key)
        if not room:
            self.error(404)
            return

        action   = self.request.GET.get('action', None)
        secret   = self.request.GET.get('secret', None)
        channel_id   = self.request.GET.get('channel_id', None)
        if not action or secret != room.secret:
            return
        
        if(room.channel_id != channel_id):
            logging.error('id %r!=%r ' % (room.channel_id, channel_id))
            return
        
        username = self.request.GET.get('username', None)
        if action == 'login':
            if not username:
                return
            last_event_id = send_event(room.channel_id, 'login %s' % username, room.event_key)
            def mod(key):
                obj = db.get(key)
                if obj.last_event_id < last_event_id:
                    obj.last_event_id = last_event_id
                if username not in obj.logged_users:
                    obj.logged_users.append(username)
                obj.put()
            db.run_in_transaction(mod, room.key())
            
        elif action == 'logout':
            if not username:
                return
            last_event_id = send_event(room.channel_id, 'logout %s' % username, room.event_key)
            def mod(key):
                obj = db.get(key)
                if obj.last_event_id < last_event_id:
                    obj.last_event_id = last_event_id
                if username in obj.logged_users:
                    obj.logged_users.remove(username)
                obj.put()
            db.run_in_transaction(mod, room.key())
        
        room = db.get(room_key)
        s = models.ListRoomsSingleton.get_singleton()
        b = utils.render_to_mako('ytalk/ytalk_list_part_part.html', {'room':room}).replace('\n','')
        
        last_event_id = send_event(s.channel_id, 'data rbox_%s %s' % (room.name, b ), s.event_key)
        def mod(key):
            obj = db.get(key)
            if obj.last_event_id == s.last_event_id:
                obj.last_event_id = last_event_id
            else:
                raise db.Rollback()
            obj.put()
        try:
            db.run_in_transaction(mod, s.key())
        except db.TransactionFailedError:
            pass

        return


